#define __KERNEL__
#include <linux/kernel.h>
#include <linux/module.h>
#include "/usr/src/linux-headers-2.6.28-17/arch/x86/include/asm/uaccess.h"
#include <linux/fs.h>
#include "vtl.h"

#define SUCCESS 0
#define DEVICE_NAME "VTL"	/* Dev name as it appears in /proc/devices   */

static int Device_Open = 0;

/*
 * This is called whenever a process attempts to open the device file
 */
static int device_open(struct inode *inode, struct file *file)
{
	/*
	 * We don't want to talk to two processes at the same time
	 */
    printk(KERN_INFO "Opening device\n");

	if (Device_Open)
		return -EBUSY;
	Device_Open++;
	
	try_module_get(THIS_MODULE);
	return SUCCESS;
}

static int device_release(struct inode *inode, struct file *file)
{
	/*
	 * We're now ready for our next caller
	 */
    printk(KERN_INFO "Releasing device\n");
	Device_Open--;

	module_put(THIS_MODULE);
	return SUCCESS;
}

/*
 * This function is called whenever a process which has already opened the
 * device file attempts to read from it.
 */
static ssize_t device_read(struct file *file,	/* see include/linux/fs.h   */
			   char __user * buffer,	/* buffer to be
							 * filled with data */
			   size_t length,	/* length of the buffer     */
			   loff_t * offset)
{
	/*
	 * Number of bytes actually written to the buffer
	 */
	int bytes_read = 0;
        char *mybuf;

        printk("Received a read request for %d bytes.",length);

        //TODO: ADD NETLINK READ AND PUT IT IN THE BUFFER

	/*
	 * Actually put the data into the buffer
	 */
	while (length && *mybuf) {

		/*
		 * Because the buffer is in the user data segment,
		 * not the kernel data segment, assignment wouldn't
		 * work. Instead, we have to use put_user which
		 * copies data from the kernel data segment to the
		 * user data segment.
		 */
		put_user(*(mybuf++), buffer++);
		length--;
		bytes_read++;
	}

	/*
	 * Read functions are supposed to return the number
	 * of bytes actually inserted into the buffer
	 */
	return bytes_read;
}

/*
 * This function is called when somebody tries to
 * write into our device file.
 */
static ssize_t
device_write(struct file *file,
	     const char __user * buffer, size_t length, loff_t * offset)
{
	int i;
        char *mybuf;

        printk("Received a write request for %d bytes", length);

        //TODO: Allocate mybuf according to length

        //This gets the buffer from userspace:
	for (i = 0; i < length; i++)
		get_user(mybuf[i], buffer + i);


        //TODO: Add writing mybuf to file via netlink

	/*
	 * Again, return the number of input characters used
	 */
	return i;
}

/*
 * This function is called whenever a process tries to do an ioctl on our
 * device file. We get two extra parameters (additional to the inode and file
 * structures, which all device functions get): the number of the ioctl called
 * and the parameter given to the ioctl function.
 *
 * If the ioctl is write or read/write (meaning output is returned to the
 * calling process), the ioctl call returns the output of this function.
 *
 */
int device_ioctl(struct inode *inode,	/* see include/linux/fs.h */
		 struct file *file,	/* ditto */
		 unsigned int ioctl_num,	/* number and param for ioctl */
		 unsigned long ioctl_param)
{

    printk(KERN_INFO "Got a IOCTL, %d", ioctl_num);

	/*
	 * Switch according to the ioctl called
	 */
	switch (ioctl_num) {
            case IOCTL_CREATE_TAPE:
                printk(KERN_INFO "CREATE_TAPE");
		break;

            case IOCTL_DELETE_TAPE:
                printk(KERN_INFO "DELETE_TAPE");
		break;

            case IOCTL_REPLACE_TAPE:
                printk(KERN_INFO "REPLACE_TAPE");
		break;

            case IOCTL_REWIND_CURRENT_TAPE:
                printk(KERN_INFO "REWIND_TAPE");
                break;
	}

	return SUCCESS;
}

/* Module Declarations */

/*
 * This structure will hold the functions to be called
 * when a process does something to the device we
 * created. Since a pointer to this structure is kept in
 * the devices table, it can't be local to
 * init_module. NULL is for unimplemented functions.
 */
struct file_operations Fops = {
	.read = device_read,
	.write = device_write,
	.ioctl = device_ioctl,
	.open = device_open,
	.release = device_release,	/* a.k.a. close */
};


/*
 * This function is called when the module is loaded
 */
int init_module(void) {
	printk(KERN_INFO "Started module.\n");

        	int ret_val;
	/*
	 * Register the character device (atleast try)
	 */
	ret_val = register_chrdev(MAJOR_NUM, DEVICE_NAME, &Fops);

	/*
	 * Negative values signify an error
	 */
	if (ret_val < 0) {
		printk(KERN_ALERT "%s failed with %d\n",
		       "Sorry, registering the character device ", ret_val);
		return ret_val;
	}

	printk(KERN_INFO "mknod %s c %d 0\n", DEVICE_FILE_NAME, MAJOR_NUM);

	return SUCCESS;
}
      

/*
 * This function is called when the module is unloaded
 */
void cleanup_module(void) {
    	
	/*
	 * Unregister the device
	 */
	unregister_chrdev(MAJOR_NUM, DEVICE_NAME);

	
}